#include "get_dt35.h"
#include <iostream>

Serial::Serial(){
    b_SimulationMode = false;
    b_OpenSign = false;
    nSerialID = -1;
}
Serial::~Serial()
{
    Close();
}

void Serial::SetSimulationMode(bool isSimulation)
{
    b_SimulationMode = isSimulation;
}


int Serial::OpenSerial(std::string SerialID,E_BaudRate Bps, E_DataSize DataSize, E_Parity Parity, E_StopBit StopBit){
    if(b_SimulationMode)
    {
        b_OpenSign = true;
        return 0;
    }
    Close();
    nSerialID = open(SerialID.c_str(),O_RDWR | O_NOCTTY | O_NONBLOCK);
    tcflush(nSerialID,TCIOFLUSH);
    if(nSerialID == -1){
        std::string str = SerialID + " open fail !!!";
        perror(str.c_str());
        return -1;
    }
    struct termios Opt;
    tcgetattr(nSerialID,&ProtoOpt);
    Opt = ProtoOpt;
    switch(Bps){
        case E_BaudRate::_115200:
            cfsetispeed(&Opt,B115200);
            cfsetospeed(&Opt,B115200);
            break;	
        case E_BaudRate::_9600:
            cfsetispeed(&Opt,B9600);
            cfsetospeed(&Opt,B9600);
            break;

        default :
            printf("Don't exist baudrate %d !\n",Bps);
            return (-1);	
    }
    Opt.c_cflag &= (~CSIZE);

    switch( DataSize )
    {
        case E_DataSize::_7:
            Opt.c_cflag |= CS7;
            break;
        case E_DataSize::_8:
            Opt.c_cflag |= CS8;
            break;
        default:
            /*perror("Don't exist iDataSize !");*/
            printf("Don't exist DataSize %d !\n",DataSize);
            return (-1);								
    }

    switch( Parity )
	{
	case E_Parity::None:					/*无校验*/
		Opt.c_cflag &= (~PARENB);
		break;
	case E_Parity::Odd:					/*奇校验*/
		Opt.c_cflag |= PARENB;
		Opt.c_cflag |= PARODD;
		Opt.c_iflag |= (INPCK | ISTRIP);
		break;
	case E_Parity::Even:					/*偶校验*/
		Opt.c_cflag |= PARENB;
		Opt.c_cflag &= (~PARODD);
		Opt.c_iflag |= (INPCK | ISTRIP);
		break;				
	default:
		/*perror("Don't exist cParity  !");*/
		printf("Don't exist Parity %c !\n",Parity);
		return (-1);								
	}

	switch( StopBit )
	{
	case E_StopBit::_1:
		Opt.c_cflag &= (~CSTOPB);
		break;
	case E_StopBit::_2:
		Opt.c_cflag |= CSTOPB;
		break;
	default:
		printf("Don't exist iStopBit %d !\n",StopBit);
		return (-1);								
	}

    Opt.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
    Opt.c_oflag &= ~OPOST;

    tcflush(nSerialID,TCIOFLUSH);
    Opt.c_cc[VTIME] = 0;
	Opt.c_cc[VMIN] = 0;
    int Result = tcsetattr(nSerialID,TCSANOW,&Opt);
    if(Result){
		perror("Set new terminal description error !");
		return -1;        
    }

    b_OpenSign = true;
    RunRecv();

    return 0;
}

int Serial::Recv(unsigned char *Buff, int length)
{

    if (b_SimulationMode) {
        int received = 0;
        while (!simulationQueue.empty() && received < length) {
            Buff[received++] = simulationQueue.front();
            simulationQueue.pop();
        }
        return received;
    }

    int res =  RefreshBuffer(Buff, length, true);
    return res;
}

void Serial::Send_from(const std::vector<uint8_t>& data)
{
    ssize_t bytes_written = write(nSerialID , data.data(), data.size());
}

int Serial::Close(){
    if(nSerialID >= 0){
        tcsetattr(nSerialID,TCSADRAIN,&ProtoOpt); 
        
    }
    close(nSerialID);
    b_OpenSign = false;
    nSerialID = -1;
    return 0;
}

void Serial::Send_to(const std::vector<uint8_t>& data)
{
    std::vector<uint8_t> data_frame = data;
    uint16_t length = data_frame.size()+4;
    std::vector<uint8_t> full_packet;
    full_packet.push_back(0x5A);
    full_packet.push_back(0xA5);
    full_packet.push_back(length & 0xFF);
    full_packet.push_back((length>>8) & 0xFF);
    full_packet.insert(full_packet.end(),data_frame.begin(),data_frame.end());
    full_packet.push_back(0x7A);
    full_packet.push_back(0xA7);
    // for(uint8_t byte:full_packet)
    // {
    //     printf("%.2x  ",byte);
    // }
    // std::cout<<"\n";
    
    ssize_t bytes_written = write(nSerialID , full_packet.data(), full_packet.size());
}

void Serial::RunRecv(){
    //std::thread ThRecv = std::thread{
    ThRecv = std::thread{
        [&](){
            unsigned char RecvBuf[1024] = {0};
            while(b_OpenSign){
               //usleep(1*1000);
                if(nSerialID < 0|| !b_OpenSign){
                    continue;
                }
                  memset(RecvBuf,0,1024);
                //std::cout<<nSerialID<<"\n";
                int res = read(nSerialID,RecvBuf,sizeof(RecvBuf));
                if(res > 0)
                {
                    RefreshBuffer(RecvBuf,res,false);
                }
            }
        }
    };
    ThRecv.detach();
}

int Serial::RefreshBuffer(unsigned char *pBuf,int Len,bool RecvTypet){
    static unsigned char Buffer[Bufflen + 1] = {0};
    static int nSum = 0;
    signed int nStop = 0;
    int ren = 0;
    if(RecvTypet == false){
        if((Len+nSum)<= Bufflen)
        {
            memcpy(&Buffer[nSum],pBuf,Len);
            nSum = Len + nSum;
        }
        else
        {
            if(Len <= Bufflen)
            {
                memcpy(Buffer,pBuf,Len);
                nSum = Len;
            }
            else
            {
                memcpy(Buffer,pBuf + (Len-Bufflen),Bufflen);
                nSum = Bufflen;
            }
        }
        ren = 0;
    }
    else{
        if(Len <= 0 ){return -1;}
        if(nSum <= 0){return 0;}
        if(Len <= nSum)
        {
            memcpy(pBuf,Buffer,Len);
            nStop = Len;
            ren = Len;
        }
        else
        {
            memcpy(pBuf,Buffer,nSum);

            nStop = nSum;
            ren = nSum;
        }
        if(nStop == 0){return 0;}
        else if(nSum > nStop)
        {
            for(int i=0; i<(nSum-nStop); i++)
            {
                Buffer[i] = Buffer[nStop + i];
            }
            nSum = nSum - nStop;           
        }
        else if(nSum == nStop)
        {nSum =0;}
    }
    return ren;
}
